package com.bys.core;

import com.bys.Const;
import com.bys.datasource.JNDIDatasource;
import com.bys.datasource.PoolDatasource;
import com.bys.datasource.UnpoolDatasource;
import com.bys.pojo.MappedStatement;
import com.bys.transaction.JDBCTransaction;
import com.bys.transaction.ManagedTransaction;
import com.bys.transaction.Transaction;
import com.bys.util.Resources;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * SqlSessionFactory 构建对象
 * 通过SqlSessionFactoryBuilder 方法来解析 godbatis-config.xml文件
 * 然后创建 SqlSession Factory 对象
 *
 * @author bys
 * @version 1.0
 * @since 1.0
 */
public class SqlSessionFactoryBuilder {
    /**
     * 无参数构造方法
     */
    public SqlSessionFactoryBuilder() {
    }

    /**
     * 解析 godbatis-config.xml 来构建SqlSessionFactory
     *
     * @param in godbatis-config.xml 文件的一个输入流
     * @return SqlSessionFactory 对象
     */
    public SqlSessionFactory build(InputStream in) throws DocumentException {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(in);
//        获取数据源对象
        DataSource dataSource = getDataSource(document);
//        获取事务管理器
        Transaction transaction = getTransaction(document,dataSource);
//        获取mapper映射
        List<String> mapperClassPath = getmapperClassPath(document);
        Map<String, MappedStatement> map = getMappedStatement(mapperClassPath);
        return new SqlSessionFactory(transaction, map);
    }

    private Map<String, MappedStatement> getMappedStatement(List<String> mapperClassPath) {
        Map<String,MappedStatement> map = new HashMap<>();
        mapperClassPath.forEach(path ->{
            SAXReader saxReader = new SAXReader();
            Document document = null;
            try {
                document = saxReader.read(Resources.getResourceAsStream(path));
                Element maperXMLElt = (Element) document.selectSingleNode("/mapper");
                    String namespace = maperXMLElt.attributeValue("namespace").trim();
                List<Element> mappers = maperXMLElt.elements();
                mappers.forEach(node ->{
                    Element mapperElt = (Element) node;
                    String id = mapperElt.attributeValue("id");
                    String sqlId = namespace + "." + id;
                    String resultType = mapperElt.attributeValue("resultType");
                    String sql = mapperElt.getTextTrim().toLowerCase();
                    MappedStatement mappedStatement = new MappedStatement(sql,resultType);
                    map.put(sqlId , mappedStatement);
                });

            } catch (DocumentException e) {
                e.printStackTrace();
            }
        });
        return map;
    }

    private List<String> getmapperClassPath(Document document) {
        List<String> mapperPaths = new ArrayList<>();
        List<Node> nodes = document.selectNodes("//mapper");
        nodes.forEach(node ->{
            String resource = ((Element) node).attributeValue("resource").trim();
            mapperPaths.add(resource);
        });
        return mapperPaths;
    }
    private Transaction getTransaction(Document document, DataSource dataSource) {
        Transaction transaction = null;
        Element tranManagerElt = (Element) document.selectSingleNode("/configuration/environments/environment/transactionManager");
        String type = tranManagerElt.attributeValue("type").trim().toUpperCase();
        switch (type){
            case Const.JDBC_TRANSACTION:
                transaction = new JDBCTransaction(dataSource,false);
                break;
            case Const.MANAGED_TRANSACTION:
                transaction = new ManagedTransaction();
                break;
        }
        return transaction;
    }

    private DataSource getDataSource(Document document) {
        DataSource dataSource = null;
        Map<String, String> map = new HashMap<>();
        List<Node> datasources = document.selectNodes("/configuration/environments/environment/dataSource/property");
        datasources.forEach(e -> {
            String name = ((Element) e).attributeValue("name");
            String value = ((Element) e).attributeValue("value");
            System.out.println(name + "   " + value);
            map.put(name, value);
        });
        Element dataSourceElt = (Element) document.selectSingleNode("/configuration/environments/environment/dataSource");
        String type = dataSourceElt.attributeValue("type").trim().toUpperCase();
        switch (type) {
            case Const.UNPOOL_DATASOURCE:
                dataSource = new UnpoolDatasource(map.get("driver"),map.get("url"),map.get("username"),map.get("password"));
                break;
            case Const.POOL_DATASOURCE:
                dataSource = new PoolDatasource();
                break;
            case Const.JNDI_DATASOURCE:
                dataSource = new JNDIDatasource();
        }
        return dataSource;
    }
}
